package com.mihe.gateway.filter;

import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mihe.gateway.result.ExceptionResult;
import com.mihe.jwt.constant.JwtAdminInfo;
import com.mihe.jwt.ignore.IgnoreProperties;
import com.mihe.jwt.util.JwtTokenUtil;
import com.mihe.redis.RedisService;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;

@Component
public class IgnoreFilter implements GlobalFilter, Ordered {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    public static String RegExp = "^/.*/api/.*";

    public static String GOODS_DETAIL = "/commodity/goodsDetail/";

    private static List<Object> ignoreUrls;

    @Resource
    RedisService redisService;

    @PostConstruct
    public void init() {
        ignoreUrls = redisService.getCacheList(IgnoreProperties.REDIS_IGNORE_KEY);
    }


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();
        //System.err.println(url);
        ///member-provider/api/test/test1
        /**
         * 如果不是C端接口 或 白名单接口，放行。
         */
        boolean isDetail = url.contains(GOODS_DETAIL);

        if (!isDetail && (!ReUtil.isMatch(RegExp, url) || isIgnore(url))) {
            return chain.filter(exchange);
        }

        String token = exchange.getRequest().getHeaders().getFirst(Header.AUTHORIZATION.getValue());
        ServerHttpResponse serverHttpResponse = exchange.getResponse();

        if (StringUtils.isBlank(token)) {
            if (url.contains(GOODS_DETAIL)) {
                return chain.filter(exchange);
            }
            return authErro(serverHttpResponse, "请登录");
        } else {
            try {
                String loginClient=exchange.getRequest().getHeaders().getFirst("loginclient");
                Collection<List<String>> headers=exchange.getRequest().getHeaders().values();
                log.info("210115-url[{}],loginClient[{}],token[{}],headers[{}]=============", url, loginClient, token, headers.toString());

                Claims claimsFromToken = JwtTokenUtil.getClaimsFromToken(token);

                String apiUserId = claimsFromToken.get(JwtAdminInfo.apiUserId).toString();
                String apiName = claimsFromToken.get(JwtAdminInfo.apiName).toString();//昵称
                String mobile = claimsFromToken.get(JwtAdminInfo.apiMobile).toString();
                String tokenKey = claimsFromToken.get(JwtAdminInfo.tokenKey).toString();
                Object jpushRegisterId = claimsFromToken.get(JwtAdminInfo.jpushRegisterId);
                String registerId=jpushRegisterId==null?"":jpushRegisterId.toString();
                Object cacheToken = redisService.getCacheObject(tokenKey);
                if (cacheToken == null) {
                    return authErro(serverHttpResponse, "token已经过期");
                }
                if (!token.equals(cacheToken)) {
                    return authErro(serverHttpResponse, "非法token");
                }

                Consumer<HttpHeaders> httpHeaders = httpHeader -> {
                    httpHeader.set(JwtAdminInfo.apiUserId, apiUserId);
                    httpHeader.set(JwtAdminInfo.apiMobile,mobile);
                    httpHeader.set(JwtAdminInfo.tokenKey,tokenKey);
                    httpHeader.set(JwtAdminInfo.jpushRegisterId,registerId);
                    try {
                        httpHeader.set(JwtAdminInfo.apiName, URLEncoder.encode(apiName, "utf-8"));
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                };
                ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
                ServerWebExchange serverWebExchange = exchange.mutate().request(serverHttpRequest).build();

                return chain.filter(serverWebExchange);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return authErro(serverHttpResponse, "认证失败");
            }
        }
    }

    private boolean isIgnore(String url) {
        int index = url.indexOf("/api");
        String substring = url.substring(index);
        for (Object ignoreUrl : ignoreUrls) {
            if (StrUtil.contains(substring, ignoreUrl.toString())) {
                return true;
            }
        }
        return false;
    }

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 认证错误输出
     *
     * @param resp 响应对象
     * @param mess 错误信息
     * @return
     */
    private Mono<Void> authErro(ServerHttpResponse resp, String mess) {
        //resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        ExceptionResult responseResult = new ExceptionResult(401, mess);

        String returnStr = "";
        try {
            returnStr = objectMapper.writeValueAsString(responseResult);
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(returnStr.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder() {
        return 1;
    }

}
